// Fine collision detection

#pragma once

#include "contacts.h"

namespace physics {

	enum PrimitiveType {
		PRIMITIVETYPE_COLLISIONPLANE,
		PRIMITIVETYPE_COLLISIONSPHERE,
		PRIMITIVETYPE_COLLISIONBOX
	};

	// forward declarations
	class IntersectionTests;
	class CollisionDetector;

	// represents a primitive to detect collisions with
	// should be used polymorphically, since 'type' will not be defined.
	class CollisionPrimitive{
	public:

		CollisionPrimitive() 
		{

		}

		virtual ~CollisionPrimitive() 
		{
		
		}

		// friend classes
		friend IntersectionTests;
		friend CollisionDetector;

		// The body that is represented by the primitive
		RigidBody *body;

		// the offset of this primitiev from the given Rigid Body
		// i.e. the bounding volume may not match the exact
		// shape of the Rigid Body
		Matrix4 offset;

		// calculate internals for the primitive
		void calculateInternals();

		// for the sake of convenience, we allow access to
		// the axis vectors in the transform
		Vector3 getAxis(unsigned index) const
		{
			return transform.getAxisVector(index);
		}
		// returns the result transformation of the primitive
		// with the offset combined and transformation of the
		// rigid body
		const Matrix4& getTransform() const
		{
			return transform;
		}

		PrimitiveType getPrimitiveType() const
		{
			return type;
		}

	protected:
		// the result transform of the primite. Calculated
		// by combining the offset with the transform
		Matrix4 transform;

		PrimitiveType type;
	};

	// A Spherical collision volume
	class CollisionSphere : public CollisionPrimitive
	{
	public:
		// radius
		real radius;

		CollisionSphere()
		{
			type = PRIMITIVETYPE_COLLISIONSPHERE;
		}

		virtual ~CollisionSphere()
		{

		}
	};

	// A plane. Does not need extra CollisionPrimitive functionality
	// but makes life easier by using it polymorphically
	class CollisionPlane : public CollisionPrimitive
	{
	public:
		// normal
		Vector3 direction;

		// distance from the origin
		real distance;

		CollisionPlane()
		{
			body = NULL;
			type = PRIMITIVETYPE_COLLISIONPLANE;
		}

		virtual ~CollisionPlane()
		{

		}

	};

	// a Box shaped collision object
	class CollisionBox : public CollisionPrimitive
	{
	public:
		// the half-sizes of the box in each axis
		Vector3 halfSize;

		CollisionBox()
		{
			type = PRIMITIVETYPE_COLLISIONBOX;
		}

		virtual ~CollisionBox()
		{

		}
	};

	// a wrapper class that holds fast intersection tests.
	// Can be used to drive the coarse collision detection system
	class IntersectionTests
	{
	public:
		static bool sphereAndHalfSpace( const CollisionSphere &sphere, const CollisionPlane &plane);
		static bool sphereAndSphere( const CollisionSphere &one, const CollisionSphere &two);
		static bool boxAndBox(const CollisionBox &one, const CollisionBox &two);

		static bool boxAndHalfSpace(const CollisionBox &box, const CollisionPlane &plane);
	};

	// used to store collision information
	struct CollisionData
	{
		// holds the base collision data. 
		Contact *contactArray;

		// holds the contact array to write into
		Contact *contacts;

		// holds the max number of contacts
		int contactsLeft;

		// holds the number of contacts found so far
		unsigned contactCount;

		// holds the frictional value
		real friction;

		// holds the restitutional value
		real restitution;

		// holds the tolerance to objects that aren't really
		// colliding, but should still generate contact information
		//real tolerance;

		// checks if there are more contacts free
		bool hasMoreContacts()
		{
			return contactsLeft > 0;
		}

		// resets the data
		void reset(unsigned maxContacts)
		{
			contactsLeft = maxContacts;
			contactCount = 0;
			contacts = contactArray;
		}

		// notified that more contact data has been added
		void addContacts(unsigned count)
		{
			// returns the number of contacts remaining
			contactsLeft -= count;
			contactCount += count;

			// push the pointer forward
			contacts += count;
		}
	};

	// wrapper class that holds fine-collision detection
	// methods
	// each function takes two objects and contact data
	class CollisionDetector
	{
	public:
		static unsigned sphereAndHalfSpace( const CollisionSphere &sphere, const CollisionPlane &plane, CollisionData *data);
		static unsigned sphereAndTruePlane( const CollisionSphere &sphere, const CollisionPlane &plane, CollisionData *data);
		static unsigned sphereAndSphere(const CollisionSphere &one, const CollisionSphere &two, CollisionData *data);
		static unsigned boxAndHalfSpace(const CollisionBox &box, const CollisionPlane &plane, CollisionData *data);
		static unsigned boxAndBox(const CollisionBox &one, const CollisionBox &two, CollisionData *data);
        static unsigned boxAndPoint(const CollisionBox &box, const Vector3 &point, CollisionData *data);
        static unsigned boxAndSphere(const CollisionBox &box, const CollisionSphere &sphere, CollisionData *data);

	};
} // namespace physics